/**
 * @file StoreCommandImpl.java
 * Projet eCOM 2010 (Miage Descartes)
 * @author Doido Sébastien
 * @version 2
 * @brief Shell : Gestion des fonctionnalités relatives aux magasins
 */
package ecom.shell;

import ecom.beans.EcomAdminRemote;
import ecom.beans.EcomCustomerRemote;
import ecom.beans.ProductStoreBean;
import ecom.beans.UnknownIdentifier;
import ecom.shell.OutputCommandImpl.Outputs;
import ecom.shell.ShellConstantes.RESULT_ACTION;
import java.io.PrintStream;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.StringTokenizer;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

/**
 * @brief Shell : Gestion des fonctionnalités relatives aux magasins
 * @author Sebastien
 */
public class StoreCommandImpl implements ShellCommand {

    private ShellContext _context = null;

    public StoreCommandImpl(ShellContext context) {
        _context = context;
    }

    /**
     * @brief Renvoie le nom de la commande
     * @return le nom de la commande
     */
    public String getName() {
        return "store";
    }

    /**
     * @brief Renvoie la manière d'utiliser la commande
     * @return la manière d'utiliser la commande
     */
    public String getUsage() {
        return "store";
    }

    /**
     * @brief Renvoie la description de la commande
     * @return la description de la commande
     */
    public String getShortDescription() {
        String description = "Store management\n";
        if (true == _context.getVar("adminMode")) {
            description += "\t  : -add [name] [city] [account ID]...........................................create a new store\n";
            description += "\t  : -remove [store ID]........................................................delete a store\n";
            description += "\t  : -addStock .p [product ID] .s [store ID] .q [quantity] .price [price]......add a new warehouseline\n";
        }
        description += "\t  : -id [store ID]............................................................display specified store details\n";
        return description;
    }

    /**
     * @brief Exécute la commande
     * @param cmdline : commande à exécuter
     * @param out : flux d'affichage std
     * @param err : flux d'affichage err
     * @return les données à afficher
     * @throws java.lang.Exception : erreur durant l'exécution de la méthode
     */
    public String execute(String cmdline, PrintStream out, PrintStream err) throws InsuffisantPrivilege {
        StringBuffer res = new StringBuffer();
        EcomCustomerRemote ecomCustomerBean = (EcomCustomerRemote) _context.getVar("customerBean");
        EcomAdminRemote ecomAdminBean = (EcomAdminRemote) _context.getVar("adminBean");
        Outputs currentOutput = (Outputs) _context.getVar("output");
        String currentCurrency = String.valueOf(_context.getVar("currency"));

        Collection<ProductStoreBean> storeList = null;

        StringTokenizer st = new StringTokenizer(cmdline, " ");

        // Ignore cmd
        st.nextToken();

        // without arg : list all stores
        if (st.countTokens() == 0) {
            storeList = ecomAdminBean.getStores();
            return printList(out, storeList, currentOutput, ecomCustomerBean, currentCurrency);
        } // With option
        else {
            String option = st.nextToken();

            // adding
            if (option.equals("-add")) {
                if (true != _context.getVar("adminMode")) {
                    throw new InsuffisantPrivilege();
                }
                if (st.countTokens() == 3) {
                    try {
                        String name = st.nextToken();
                        String city = st.nextToken();
                        int accountID = Integer.parseInt(st.nextToken());

                        int id = ecomAdminBean.createProductStore(accountID, name, city);
                        res.append(_context.returnCode("Store Created with id #" + id, RESULT_ACTION.SUCCESS));
                    } catch (UnknownIdentifier ex) {
                        res.append(_context.returnCode(ex.toString(), RESULT_ACTION.FAILED));
                        err.println(ex);
                    } catch (Exception ex) {
                        res.append(_context.returnCode(ex.toString(), RESULT_ACTION.FAILED));
                        err.println("Error with store creation");
                    }
                } else {
                    res.append(_context.returnCode(Shell.INCORRECT_ARGS_NUMBER, RESULT_ACTION.FAILED));
                    err.println(Shell.INCORRECT_ARGS_NUMBER);
                }
            } // removing
            else if (option.equals("-remove")) {
                if (true != _context.getVar("adminMode")) {
                    throw new InsuffisantPrivilege();
                }
                if (st.countTokens() == 1) {
                    try {
                        int id = Integer.valueOf(st.nextToken());
                        ecomAdminBean.removeStore(id);
                        res.append(_context.returnCode("Store #" + id + " deleted", RESULT_ACTION.SUCCESS));
                    } catch (UnknownIdentifier ex) {
                        res.append(_context.returnCode(ex.toString(), RESULT_ACTION.FAILED));
                        err.println(ex);
                    } catch (Exception ex) {
                        res.append(_context.returnCode(ex.toString(), RESULT_ACTION.FAILED));
                        err.println("Error with store remove" + ex);
                    }
                } else {
                    res.append(_context.returnCode(Shell.INCORRECT_ARGS_NUMBER, RESULT_ACTION.FAILED));
                    err.println(Shell.INCORRECT_ARGS_NUMBER);
                }
            } else if (option.equals("-id")) {
                if (st.countTokens() == 1) {
                    try {
                        int id = Integer.valueOf(st.nextToken());
                        ProductStoreBean store = ecomCustomerBean.getStoreById(id);
                        return printStore(out, store, currentOutput, ecomCustomerBean, currentCurrency);
                    } catch (UnknownIdentifier ex) {
                        res.append(_context.returnCode(ex.toString(), RESULT_ACTION.FAILED));
                        err.println(ex);
                    } catch (Exception ex) {
                        res.append(_context.returnCode(ex.toString(), RESULT_ACTION.FAILED));
                        err.println("Error with store detail");
                    }
                } else {
                    res.append(_context.returnCode(Shell.INCORRECT_ARGS_NUMBER, RESULT_ACTION.FAILED));
                    err.println(Shell.INCORRECT_ARGS_NUMBER);
                }
            } else if (option.equals("-addStock")) {
                if (true != _context.getVar("adminMode")) {
                    throw new InsuffisantPrivilege();
                }
                if (st.countTokens() == 8) {
                    try {
                        if (".p".equalsIgnoreCase(st.nextToken().trim())) {
                            int productId = Integer.valueOf(st.nextToken().trim());
                            if (".s".equalsIgnoreCase(st.nextToken().trim())) {
                                int storeId = Integer.valueOf(st.nextToken().trim());
                                if (".q".equalsIgnoreCase(st.nextToken().trim())) {
                                    int q = Integer.valueOf(st.nextToken().trim());
                                    if (".price".equalsIgnoreCase(st.nextToken().trim())) {
                                        double price = Double.valueOf(st.nextToken().trim());
                                        ecomAdminBean.createStockLine(storeId, productId, q, price);
                                        res.append(_context.returnCode("STOCK ADDED", RESULT_ACTION.SUCCESS));
                                    }
                                }
                            }
                        }
                    } catch (UnknownIdentifier ex) {
                        res.append(_context.returnCode(ex.toString(), RESULT_ACTION.FAILED));
                        err.println(ex);
                    } catch (Exception ex) {
                        res.append(_context.returnCode(ex.toString(), RESULT_ACTION.FAILED));
                        err.println("Error with store warehouse update");
                    }
                } else {
                    res.append(_context.returnCode(Shell.INCORRECT_ARGS_NUMBER, RESULT_ACTION.FAILED));
                    err.println(Shell.INCORRECT_ARGS_NUMBER);
                }
            } else {
                err.println("\t" + option + " is not known for Account Management");
            }
        }
        return res.toString();
    }

    /**
     * @brief Affichage de la liste des magasins
     * @param out : flux d'affichage std
     * @param symbols : liste des magasins disponibles
     * @param currentOutput : type de sortie attendue
     * @return les données à afficher selon le type de sortie attendue
     */
    private String printList(PrintStream out, Collection<ProductStoreBean> symbols, Outputs currentOutput, EcomCustomerRemote ecomCustomerBean, String currentCurrency) {
        StringBuffer res = new StringBuffer();
        if (currentOutput.equals(Outputs.XML)) {
            Element accounts = new Element("stores");
            org.jdom.Document document = new Document(accounts);

            for (ProductStoreBean store : symbols) {
                accounts.addContent(buildXMLStoreNode(store, true, ecomCustomerBean, currentCurrency));
            }
            XMLOutputter sortie = new XMLOutputter(Format.getPrettyFormat());
            return sortie.outputString(document);
        } else if (currentOutput.equals(Outputs.TEXT)) {
            res.append("################" + "\n");
            res.append("# Known stores #" + "\n");
            res.append("################" + "\n");
            for (ProductStoreBean store : symbols) {
                res.append(printTEXT(out, store) + "\n");
            }
        } else {
            res.append("\t /!\\ Output format '" + currentOutput + "' is known but not implementing for this command");
        }
        return res.toString();
    }

    /**
     * @brief Affiche un magasin
     * @param out : flux d'affichage std
     * @param store : magasin à afficher
     * @param currentOutput : sortie désirée
     * @return les données à afficher selon le type de sortie positionné
     */
    private String printStore(PrintStream out, ProductStoreBean store, Outputs currentOutput, EcomCustomerRemote ecomCustomerBean, String currentCurrency) {
        Collection<ProductStoreBean> list = new ArrayList();
        list.add(store);
        return printList(out, list, currentOutput, ecomCustomerBean, currentCurrency);
    }

    /**
     * @brief Affiche un magasin (TEXT)
     * @param out : flux d'affichage std
     * @param store : magasin à afficher
     * @return les données à afficher selon le type de sortie positionné
     */
    private String printTEXT(PrintStream out, ProductStoreBean store) {
        return ("\t" + store);
    }

    /**
     * @brief Renvoie un noeud XML décrivant le magasin à afficher
     * @param s : le magasin à afficher
     * @param withAccount : affichage des données détaillées du compte ou pas
     * @return le noeud XML décrivant le magasin à afficher
     */
    public static Element buildXMLStoreNode(ProductStoreBean s, boolean withAccount, EcomCustomerRemote ecomCustomerBean, String currentCurrency) {
        Element store = new Element("store");
        Attribute id = new Attribute("id", String.valueOf(s.getReference()));
        store.setAttribute(id);

        Element name = new Element("name");
        name.setText(s.getName());
        store.addContent(name);

        Element city = new Element("city");
        city.setText(s.getCity());
        store.addContent(city);

        if (withAccount) {
            Element account = new Element("account");
            Attribute accountId = new Attribute("id", String.valueOf(s.getAccount().getAccountId()));
            account.setAttribute(accountId);
            store.addContent(account);

            Element accountName = new Element("name");
            accountName.setText(s.getAccount().getAccountOwner());
            account.addContent(accountName);

            Element surname = new Element("surname");
            surname.setText(s.getAccount().getSurname());
            account.addContent(surname);

            Element balance = new Element("balance");
            DecimalFormat myFormatter = new DecimalFormat("##0.00");
            balance.setText(myFormatter.format(ecomCustomerBean.convertFromEuro(s.getAccount().getBalance(), currentCurrency)));
            Attribute currency = new Attribute("currency", currentCurrency);
            balance.setAttribute(currency);
            account.addContent(balance);
        }
        return store;
    }
}
